package com.only4play.json.converter;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Pattern;

/**
 * @author liyuncong
 * @version 1.0
 * @file CustomJsonDeserializer
 * @brief CustomJsonDeserializer
 * @details CustomJsonDeserializer
 * @date 2022-06-24
 *
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                        NAME               DESCRIPTION
 * 2022-06-24                      liyuncong          Created
 */
@Slf4j
public class CustomJsonDeserializer {

    public static final CustomDateJsonDeserializer dateJsonDeserializer =
        new CustomDateJsonDeserializer();

    private static class CustomDateJsonDeserializer extends JsonDeserializer<LocalDateTime> {
        private static final Pattern PATTERN_DIGITS = Pattern.compile("\\d+");
        private static final String CHAR_T = "T";
        private static final String CHAR_Z = "Z";
        private static final String PLUS = "+";
        private static final int defaultOffsetMs = TimeZone.getDefault().getRawOffset();
        private static final ZoneOffset defaultOffsetObj =
            ZoneOffset.ofTotalSeconds(defaultOffsetMs / 1000);
        private static final LocalDateTimeDeserializer fallbackDeserializer =
            LocalDateTimeDeserializer.INSTANCE;

        @Override
        public LocalDateTime deserialize(JsonParser parser, DeserializationContext context)
            throws IOException, JacksonException {
            try {
                String content = parser.getText();
                if (PATTERN_DIGITS.matcher(content).matches()) {
                    return LocalDateTime.from(new Date(Long.parseLong(content)).toInstant());
                } else if (content.contains(CHAR_T) && content.endsWith(CHAR_Z)) {
                    return LocalDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(content))
                        .atOffset(ZoneOffset.UTC).atZoneSameInstant(ZoneId.systemDefault())
                        .toLocalDateTime();
                } else if (content.contains(CHAR_T) && !content.contains(PLUS) &&
                    !content.contains(CHAR_Z)) {
                    return LocalDateTime.from(
                        DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(
                            content + defaultOffsetObj.toString()));
                } else {
                    return LocalDateTime.from(
                        DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(content));
                }
            } catch (Throwable throwable) {
                log.warn("Failed to deserialize local datetime", throwable);
                return fallbackDeserializer.deserialize(parser, context);
            }
        }
    }

}
